#include "fh_spi.h"
#include "fh_spi_extend.h"

int fh_spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
                void *din, unsigned long flags);

struct s_spi_cs_info g_spi_cs_info[SPI_MAX_BUS_NUM] = {
	{
		.num = SPI0_MAX_CS_NUM,
		.cs[0] = SPI0_CS0,
#if SPI0_MAX_CS_NUM > 1
		.cs[1] = SPI0_CS1,
#endif
	},

#if SPI_MAX_BUS_NUM > 1
	{
		.num = SPI1_MAX_CS_NUM,
		.cs[0] = SPI1_CS0,
#if SPI1_MAX_CS_NUM > 1
		.cs[1] = SPI1_CS1,
#endif
	},
#endif

};

struct s_spi_hand_shake_info g_spi_handshake_info[SPI_MAX_BUS_NUM] = {
	{
		.hw_tx = SPI0_TX,
		.hw_rx = SPI0_RX,
	},
#if SPI_MAX_BUS_NUM > 1
	{
		.hw_tx = SPI1_TX,
		.hw_rx = SPI1_RX,
	},
#endif
};

struct s_spi_dma_master_info g_spi_dma_master_info[SPI_MAX_BUS_NUM] = {
	{
		.dma_ctl_master_sel = SPI0_DMA_CTL_MASTER_SEL,
		.dma_mem_master_sel = SPI0_MEM_CTL_MASTER_SEL,
	},
#if SPI_MAX_BUS_NUM > 1
	{
		.dma_ctl_master_sel = SPI1_DMA_CTL_MASTER_SEL,
		.dma_mem_master_sel = SPI1_MEM_CTL_MASTER_SEL,
	},
#endif
};

const unsigned int FH_SPI_BASE[] = {
	SPI0_REG_BASE,
	SPI1_REG_BASE,
};

SINT32 Spi_Enable(UINT32 base, spi_enable_e enable)
{
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, enable);
	SET_REG(REG_SPI_SSIENR(base), enable);
	return CONFIG_OK;
}

void SPI_EnableDma(UINT32 base, unsigned int channel)
{
	unsigned int reg;
	reg = GET_REG(REG_SPI_DMACR(base));
	reg |= channel;
	SET_REG(REG_SPI_DMACR(base), reg);
}

void SPI_DisableDma(UINT32 base, unsigned int channel)
{
	unsigned int reg;
	reg = GET_REG(REG_SPI_DMACR(base));
	reg &= ~channel;
	SET_REG(REG_SPI_DMACR(base), reg);
}

void SPI_ContinueReadNum(UINT32 base,unsigned int  num)
{
	SET_REG(REG_SPI_CTRLR1(base), (num -1));
}

SINT32 Spi_SetPolarity(UINT32 base, spi_polarity_e polarity)
{
	UINT32 data;

	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, polarity);
	data = GET_REG(REG_SPI_CTRLR0(base));
	data &= ~(UINT32) SPI_POLARITY_RANGE;
	data |= polarity;
	SET_REG(REG_SPI_CTRLR0(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetPhase(UINT32 base, spi_phase_e phase)
{
	UINT32 data;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, phase);
	data = GET_REG(REG_SPI_CTRLR0(base));
	data &= ~(UINT32) SPI_PHASE_RANGE;
	data |= phase;
	SET_REG(REG_SPI_CTRLR0(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetFrameFormat(UINT32 base, spi_format_e format)
{
	UINT32 data = 0;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, format);
	data = GET_REG(REG_SPI_CTRLR0(base));
	data &= ~(UINT32) SPI_FRAME_FORMAT_RANGE;
	data |= format;
	SET_REG(REG_SPI_CTRLR0(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetDataSize(UINT32 base, spi_data_size_e size)
{
	UINT32 data = 0;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, size);
	data = GET_REG(REG_SPI_CTRLR0(base));
	data &= ~(UINT32) SPI_DATA_SIZE_RANGE;
	data |= size;
	SET_REG(REG_SPI_CTRLR0(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetTransferMode(UINT32 base, spi_transfer_mode_e mode)
{
	UINT32 data = 0;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, mode);
	data = GET_REG(REG_SPI_CTRLR0(base));
	data &= ~(UINT32) SPI_TRANSFER_MODE_RANGE;
	data |= mode;
	SET_REG(REG_SPI_CTRLR0(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetContinueReadDataNum(UINT32 base, UINT32 num)
{
	UINT32 data = 0;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, num);
	data = num - 1;
	SET_REG(REG_SPI_CTRLR1(base), data);
	return CONFIG_OK;
}

SINT32 Spi_SetBaudrate(UINT32 base, spi_baudrate_e baudrate)
{
	if(baudrate % 2 != 0)
		FH_SPI_DEBUG("[Warnning :] div in [%d] should an even number..\n",baudrate);
	SET_REG(REG_SPI_BAUDR(base), baudrate);
	return CONFIG_OK;
}

SINT32 Spi_SetSampleDelay(UINT32 base,  UINT32 delay)
{
	SET_REG(REG_SPI_SSI_SAMPLE_DELAY(base), delay);
	return CONFIG_OK;
}

SINT32 Spi_DisableIrq(UINT32 base, UINT32 irq)
{
	UINT32 data = 0;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, irq);
	data = GET_REG(REG_SPI_IMR(base));
	data &= ~irq;
	SET_REG(REG_SPI_IMR(base), data);
	return CONFIG_OK;
}


SINT32 Spi_ReadStatus(UINT32 base)
{
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, base);
	return  GET_REG(REG_SPI_SR(base));
}

SINT32 Spi_EnableSlaveen(UINT32 base, int bus, int cs)
{
	UINT32 data;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, cs);

	gpio_direction_output(g_spi_cs_info[bus].cs[cs], 0);

	data = GET_REG(REG_SPI_SER(base));
	data |= cs + SPI_SLAVE_PORT0;
	SET_REG(REG_SPI_SER(base), data);
	return CONFIG_OK;
}

static UINT32 Spi_ReadTxfifolevel(UINT32 base)
{
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, base);
	return GET_REG(REG_SPI_TXFLR(base));
}

SINT32 Spi_DisableSlaveen(UINT32 base, int bus, int cs)
{
	UINT32 data;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, cs);
	gpio_direction_output(g_spi_cs_info[bus].cs[cs], 1);
	data = GET_REG(REG_SPI_SER(base));
	data &= ~(cs + SPI_SLAVE_PORT0);
	SET_REG(REG_SPI_SER(base), data);
	return CONFIG_OK;
}

void SPI_SetTxLevel(UINT32 base, UINT32 level)
{
	SET_REG(REG_SPI_TXFTLR(base), level);
}

void SPI_SetRxLevel(UINT32 base, UINT32 level)
{
	SET_REG(REG_SPI_RXFTLR(base), level);
}

UINT32 SPI_GetTxLevel(UINT32 base)
{
	return GET_REG(REG_SPI_TXFTLR(base));
}

UINT32 SPI_GetRxLevel(UINT32 base)
{
	return GET_REG(REG_SPI_RXFTLR(base));
}

UINT32 Spi_SetRxCaptureMode(UINT32 base, UINT32 enable)
{
	UINT32 data;

	data = GET_REG(REG_SPI_CCFGR(base));
	data &= ~(0x1 << 21);
	data = enable ? (data | (0x1 << 21)) : (data & ~(0x1 << 21));
	SET_REG(REG_SPI_CCFGR(base), data);

	return CONFIG_OK;
}

static inline UINT32 tx_max_tx_only(fh_spi_controller *p_fh_spi, UINT32 tx_len)
{
	UINT32 hw_tx_level;
	FH_SPI_DEBUG("sf %s(%x,%d)\n", __func__, base, tx_len);
	hw_tx_level = Spi_ReadTxfifolevel(p_fh_spi->base);
	hw_tx_level = p_fh_spi->tx_fifo_len - hw_tx_level;
	return min(hw_tx_level, tx_len);
}

static inline UINT32 tx_max(fh_spi_controller *p_fh_spi, UINT32 tx_total_len)
{
	UINT32 hw_tx_level, hw_rx_level;
	UINT32 temp_tx_lev;
	UINT32 rx_lev_reg, tx_lev_reg;
	UINT32 base = p_fh_spi->base;
	rx_lev_reg = base + SPI_RXFL_OFFSET;
	tx_lev_reg = base + SPI_TXFL_OFFSET;

	temp_tx_lev = GET_REG(tx_lev_reg);
	hw_rx_level = temp_tx_lev + GET_REG(rx_lev_reg);
	if (hw_rx_level >= p_fh_spi->tx_fifo_len)
		return 0;
	hw_rx_level++;
	hw_tx_level = temp_tx_lev;
	hw_tx_level = p_fh_spi->tx_fifo_len - hw_tx_level;
	hw_rx_level = p_fh_spi->tx_fifo_len - hw_rx_level;
	return min(min(hw_tx_level, tx_total_len), hw_rx_level);
}

int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
	if (bus >= SPI_MAX_BUS_NUM)
		return 0;
	if (cs >= g_spi_cs_info[bus].num)
		return 0;
	return 1;
}

void fh_spi_check_rxfifo_depth(struct _fh_spi_controller *spi_control)
{
    UINT32 w_i, r_i, ori;

    ori = SPI_GetRxLevel(spi_control->base);
    for (r_i = w_i = RX_FIFO_MIN_LEN; w_i < RX_FIFO_MAX_LEN; r_i++)
    {
        SPI_SetRxLevel(spi_control->base, ++w_i);
        if (r_i == SPI_GetRxLevel(spi_control->base))
            break;
    }
    SPI_SetRxLevel(spi_control->base, ori);
    spi_control->rx_fifo_len = r_i + 1;

}

void fh_spi_check_txfifo_depth(struct _fh_spi_controller *spi_control)
{
    UINT32 w_i, r_i, ori;
    ori = SPI_GetTxLevel(spi_control->base);
    for (r_i = w_i = TX_FIFO_MIN_LEN; w_i < TX_FIFO_MAX_LEN; r_i++)
    {
        SPI_SetTxLevel(spi_control->base, ++w_i);
        if (r_i == SPI_GetTxLevel(spi_control->base))
            break;
    }
    SPI_SetTxLevel(spi_control->base, ori);
    spi_control->tx_fifo_len = r_i + 1;
}


void fh_spi_cs_activate(struct spi_slave *slave)
{
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, slave);
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	Spi_EnableSlaveen(fh_spi->base, fh_spi->slave.bus, fh_spi->slave.cs);
}

void fh_spi_cs_deactivate(struct spi_slave *slave)
{
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, slave);
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	Spi_DisableSlaveen(fh_spi->base, fh_spi->slave.bus, fh_spi->slave.cs);
}


void fh_spi_free_slave(struct spi_slave *slave)
{
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, slave);
	free(fh_spi);
}

int fh_spi_claim_bus(struct spi_slave *slave)
{
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	if (!fh_spi)
		return -1;
	Spi_Enable(fh_spi->base, SPI_DISABLE);
	Spi_SetSampleDelay(fh_spi->base, fh_spi->sample_dly);
	//frame format
	Spi_SetFrameFormat(fh_spi->base, SPI_MOTOROLA_MODE);
	//spi polarty
	Spi_SetPolarity(fh_spi->base, SPI_POLARITY_HIGH);
	//spi phase
	Spi_SetPhase(fh_spi->base, SPI_PHASE_TX_FIRST);
	//transfer data size
	Spi_SetDataSize(fh_spi->base, SPI_DATA_SIZE_8BIT);
	//baudrate
	Spi_SetBaudrate(fh_spi->base, fh_spi->freq);
	(void) Spi_DisableIrq(fh_spi->base, SPI_IRQ_ALL);
	Spi_SetTransferMode(fh_spi->base, SPI_TX_RX_MODE);
	if(fh_spi->base==SPI0_REG_BASE) {
	spi_hw_init(&fh_spi->slave, slave->bus);
	}
#ifdef CONFIG_FH_SPI_ECC
	spi_hw_ecc_init(&fh_spi->slave);
#endif
	Spi_SetRxCaptureMode(fh_spi->base, SPI_DISABLE);
	Spi_Enable(fh_spi->base, SPI_ENABLE);

	return 0;
}

void fh_spi_release_bus(struct spi_slave *slave)
{
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	FH_SPI_DEBUG("sf %s(%x)\n", __func__, slave);
	Spi_Enable(fh_spi->base, SPI_DISABLE);

	Spi_Enable(fh_spi->base, SPI_ENABLE);
}

struct spi_slave *fh_spi_setup_slave(unsigned int bus, unsigned int cs,
				unsigned int max_hz, unsigned int mode)
{
	fh_spi_controller *fh_spi;
	FH_SPI_DEBUG("sf %s(%d,%d,%d,%d)\n", __func__, bus, cs, max_hz, mode);
	if (!spi_cs_is_valid(bus, cs))
		return NULL;

	if (max_hz > (SPI_SCLKIN / 2)) {
		printf("max set baud is %d\n",SPI_SCLKIN / 2);
		return NULL;
	}

	fh_spi = malloc(sizeof(fh_spi_controller));
	if (!fh_spi)
		return NULL;
	memset(fh_spi, 0, sizeof(fh_spi_controller));
	//para is default by test
	fh_spi->clk_in = SPI_SCLKIN;
	fh_spi->freq = fh_spi->clk_in / max_hz; 
#ifdef CONFIG_FH_SPI_DTR
	fh_spi->sample_dly = 2;
#else
	fh_spi->sample_dly = 1;
#endif
	fh_spi->base = FH_SPI_BASE[bus]; /* SPI0_PORT; */
	fh_spi->mode = mode;
	fh_spi->slave.bus = bus;
	fh_spi->slave.cs  = cs;
	fh_spi->slave.max_read_size     = 0;
	fh_spi->slave.max_write_size    = 0;
	fh_spi->slave.spi_xfer          = fh_spi_xfer;
	fh_spi->slave.spi_claim_bus     = fh_spi_claim_bus;
	fh_spi->slave.spi_release_bus   = fh_spi_release_bus;
	fh_spi->slave.spi_cs_activate   = fh_spi_cs_activate;
	fh_spi->slave.spi_cs_deactivate = fh_spi_cs_deactivate;
	fh_spi->rx_hw_hand = g_spi_handshake_info[bus].hw_rx;
	fh_spi->tx_hw_hand = g_spi_handshake_info[bus].hw_tx;
	fh_spi->dma_ctl_master_sel = g_spi_dma_master_info[bus].dma_ctl_master_sel;
	fh_spi->dma_mem_master_sel = g_spi_dma_master_info[bus].dma_mem_master_sel;
	FH_SPI_DEBUG("master [ctl : mem] = [%d : %d]\n",fh_spi->dma_ctl_master_sel,fh_spi->dma_mem_master_sel);
	fh_spi_check_rxfifo_depth(fh_spi);
	fh_spi_check_txfifo_depth(fh_spi);
	spi_cs_deactivate(&fh_spi->slave);
	FH_SPI_DEBUG(" fh_spi freq=%d, base=%x, mode=%d, bus=%d, cs=%d\n",
				fh_spi->freq, fh_spi->base, fh_spi->mode,
				fh_spi->slave.bus, fh_spi->slave.cs);

	return &fh_spi->slave;
}

int spi_change_bus(struct spi_slave *slave)
{
	fh_spi_controller *fh_spi = to_fh_spi(slave);

	Spi_Enable(fh_spi->base, SPI_DISABLE);
	switch (fh_spi->mode) {
	case SPI_MODE_0:
		//spi polarty
		Spi_SetPolarity(fh_spi->base, SPI_POLARITY_LOW);
		//spi phase
		Spi_SetPhase(fh_spi->base, SPI_PHASE_RX_FIRST);
		break;
	case SPI_MODE_3:
		//spi polarty
		Spi_SetPolarity(fh_spi->base, SPI_POLARITY_HIGH);
		//spi phase
		Spi_SetPhase(fh_spi->base, SPI_PHASE_TX_FIRST);
		break;
	case SPI_MODE_1:
		//spi polarty
		Spi_SetPolarity(fh_spi->base, SPI_POLARITY_LOW);
		//spi phase
		Spi_SetPhase(fh_spi->base, SPI_PHASE_TX_FIRST);
		break;
	case SPI_MODE_2:
		//spi polarty
		Spi_SetPolarity(fh_spi->base, SPI_POLARITY_HIGH);
		//spi phase
		Spi_SetPhase(fh_spi->base, SPI_PHASE_RX_FIRST);
		break;
	default:
		printf("not support to change in uboot\n");
		break;
	}
	Spi_SetBaudrate(fh_spi->base, fh_spi->freq);

	Spi_Enable(fh_spi->base, SPI_ENABLE);
	return 0;
}

void spi_16bit_check_data(unsigned short *rxp,unsigned int len)
{
	if(((unsigned int)rxp % 2) || (len % 2))
	{
		printf("rx buf : 0x%x or len : 0x%x should 2 byte allign\n",(unsigned int)rxp,len);
		FH_SPI_ASSERT(0);
	}
}

void spi_32bit_check_data(unsigned int *rxp,unsigned int len)
{
	if(((unsigned int)rxp % 4) || (len % 4))
	{
		printf("rx buf : 0x%x or len : 0x%x should 4 byte allign\n",(unsigned int)rxp,len);
		FH_SPI_ASSERT(0);
	}
}

int fh_spi_high_speed_rx_process(fh_spi_controller *p_fh_spi, uchar *rxp, uint len, uint byte_width)
{
	uint time_out = 0xffff * CONFIG_SYS_HZ;
	unsigned long timebase;
	uint error_status;
	uint base = p_fh_spi->base;
	uint data_addr = base + SPI_DATA_OFFSET;
	register uint rx_one_time = 0;
	uint i;
	unsigned short *p16_rx;
	unsigned int   *p32_rx;
	p16_rx = (unsigned short *)rxp;
	p32_rx = (unsigned int *)rxp;
	rx_one_time = len;

	Spi_Enable(base, SPI_DISABLE);
	Spi_SetTransferMode(base, SPI_ONLY_RX_MODE);
	Spi_SetContinueReadDataNum(base, len);
	Spi_Enable(base, SPI_ENABLE);
	//set dummy data;
	SET_REG(data_addr, 0xffffffff);
	//set read data no
	timebase = get_timer(0);
	while (len) {
		rx_one_time = GET_REG(REG_SPI_RXFLR(base));
		len -= rx_one_time;

		if (byte_width == 32) {
			for (i = 0; i < rx_one_time; i++) {
				*p32_rx++ =(unsigned int)GET_REG(data_addr);
			}
		} else if(byte_width == 16){
			for (i = 0; i < rx_one_time; i++) {
				*p16_rx++ =(unsigned short)GET_REG(data_addr);
			}
		} else{
			for (i = 0; i < rx_one_time; i++) {
				*rxp++ = GET_REG(data_addr);
			}
		}

		if (get_timer(timebase) > time_out) {
			printf("len is %d\n", len);

			error_status = GET_REG(base + 0x34);
			printf("error is %d\n", error_status);
			return 0;
		}
	}
	return 0;
}

int fh_spi_tx_rx_handle(fh_spi_controller *p_fh_spi, uchar *src_txp, uchar *src_rxp, uint len)
{
	register uint rx_fifo_capability, tx_fifo_capability;
	uchar *rxp;
	uchar *txp;
	register uint dummy_data;
	uint rx_xfer_len;
	uint tx_xfer_len;
	uint base = p_fh_spi->base;
	tx_xfer_len = rx_xfer_len = len;
	rxp = src_rxp;
	txp = src_txp;

	register uint data_reg;
	uint rx_lev_reg;
	data_reg = base + SPI_DATA_OFFSET;
	rx_lev_reg = base + SPI_RXFL_OFFSET;

	goto first;

start:
	rx_fifo_capability = GET_REG(rx_lev_reg);
	rx_xfer_len -= rx_fifo_capability;
	if (rxp != NULL) {
		while (rx_fifo_capability) {
			*rxp++ = (uchar) GET_REG(data_reg);
			rx_fifo_capability--;
		}

	} else {
		while (rx_fifo_capability) {
			dummy_data = GET_REG(data_reg);
			rx_fifo_capability--;
		}
	}

	if (rx_xfer_len == 0) {
		return 0;
	}

first:
	tx_fifo_capability = tx_max(p_fh_spi, tx_xfer_len);

	tx_xfer_len -= tx_fifo_capability;
	if (txp != NULL) {

		while (tx_fifo_capability) {
			SET_REG(data_reg, *txp++);
			tx_fifo_capability--;
		}

	} else {
		while (tx_fifo_capability) {
			SET_REG(data_reg, 0xff);
			tx_fifo_capability--;
		}
	}

	goto start;
}

#if defined CONFIG_FH_DMA || defined CONFIG_FH_AXI_DMA || defined CONFIG_FH_MC_DMA
void dma_spi_rx_para(fh_spi_controller *p_fh_spi,struct dma_transfer *trans,struct spi_advanced_info *spi_info,
unsigned char *dst_add,unsigned int base,unsigned int transfer_size,unsigned int hw_hand)
{
	struct spi_slave *slave = &p_fh_spi->slave;
	trans->channel_number = 0;
	trans->fc_mode = DMA_P2M;
	trans->dst_add = (u32) dst_add;
	trans->dst_inc_mode = DW_DMA_SLAVE_INC;
	trans->dst_msize = DW_DMA_SLAVE_MSIZE_8;

	/*ctl master and mem master sel*/
	trans->src_master_sel = p_fh_spi->dma_ctl_master_sel;
	trans->dst_master_sel = p_fh_spi->dma_mem_master_sel;

	if (spi_info->data_reg_offset != 0)
		trans->src_add = (u32)(base + spi_info->data_reg_offset);
	else
		trans->src_add = (u32)(base + SPI_DATA_OFFSET);

	if (spi_info->data_increase_support == INC_SUPPORT) {
		trans->src_inc_mode = DW_DMA_SLAVE_INC;
		trans->period_len = spi_info->data_field_size;
		trans->src_reload_flag = ADDR_RELOAD;
		trans->dst_width = DW_DMA_SLAVE_WIDTH_32BIT;
	} else {
		trans->src_inc_mode = DW_DMA_SLAVE_FIX;
		trans->period_len = 0;
		trans->src_reload_flag = 0;
		trans->dst_width = DW_DMA_SLAVE_WIDTH_8BIT;
	}

	trans->src_msize = DW_DMA_SLAVE_MSIZE_8;
	trans->src_per   = hw_hand;
	trans->trans_len = transfer_size;

	if (slave->mode & SPI_32BIT_XFER){
		trans->src_width = DW_DMA_SLAVE_WIDTH_32BIT;
		trans->period_len = trans->period_len / 4;
	}
	else if (slave->mode & SPI_16BIT_XFER){
		trans->src_width = DW_DMA_SLAVE_WIDTH_16BIT;
		trans->period_len = trans->period_len / 2;
	}
	else
		trans->src_width = DW_DMA_SLAVE_WIDTH_8BIT;
#ifdef CONFIG_FH_MC_DMA
	int width = ((slave->mode & SPI_32BIT_XFER) ? 32 : (slave->mode & SPI_16BIT_XFER) ? 16 : 8);
	trans->data_switch  = SWT_ABCD_ABCD;
	trans->ot_len_flag  = USR_DEFINE_ONE_TIME_LEN;
	trans->src_msize    = (DMA_RX_LEVEL + 1) * (width / 8);
#endif
}

void spi_xfer_dma_rx_only(fh_spi_controller *p_fh_spi,unsigned char *rx_add,unsigned int size){
	int id = 0;
	struct spi_advanced_info *spi_info;
	unsigned int hand_shake;
	unsigned int base = p_fh_spi->base;
	void* dma_rx_lli;
	struct dma_transfer rx_transfer = {0};
	memset(&rx_transfer,0,sizeof(struct dma_transfer));
	if(base == SPI0_REG_BASE){
		id = 0;
	}
	else{
		id = 1;
	}

	hand_shake = p_fh_spi->rx_hw_hand;
	dma_rx_lli = get_dma_rx_lli_head();
	spi_info = get_spi_info(id);
	dma_spi_rx_para(p_fh_spi, &rx_transfer, spi_info, rx_add, base, size, hand_shake);
	handle_single_transfer(&rx_transfer,dma_rx_lli);
	Spi_Enable(base, SPI_DISABLE);
	Spi_SetTransferMode(base, SPI_ONLY_RX_MODE);
	SET_REG(REG_SPI_DMARDLR(base), DMA_RX_LEVEL);
	SPI_ContinueReadNum(base,size);
	Spi_Enable(base,SPI_ENABLE);
#ifdef CONFIG_FH_SPI_ECC
	if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE)
		Spi_EccEnable(base, 1);
#endif
	SPI_EnableDma(base,SPI_RX_DMA);
	SET_REG(base + SPI_DATA_OFFSET, 0xffffffff);
	//then wait dma done...
	wait_dma_xfer_done(rx_transfer.channel_number);
	SPI_DisableDma(base,SPI_RX_DMA);
#ifdef CONFIG_FH_SPI_ECC
	if (p_fh_spi->ecc_status & SPI_XFER_ECC_ENCODE) 
		Spi_EccEnable(base, 0);
#endif
}
#endif


int fh_spi_high_speed_xfer(fh_spi_controller *p_fh_spi, uchar *rxp, const uchar *txp, uint len,uint mode)
{
	uint xfer_one_time = 0;
	uint i;
	uint base = p_fh_spi->base;
	uint data_addr = base + SPI_DATA_OFFSET;
	uint status;
#if defined CONFIG_FH_DMA || defined CONFIG_FH_AXI_DMA || defined CONFIG_FH_MC_DMA
	uint index = 0;
	uint otp_xfer_bytes = 0;
	uint limit = SPI_RX_ONLY_ONE_TIME_SIZE;
#endif
	uint  byte_width = 8;
	struct spi_slave *slave = &p_fh_spi->slave;
	unsigned short *p_16_rx;
	unsigned int   *p_32_rx;

	if (mode == SPI_ONLY_TX_MODE) {
		Spi_Enable(base, SPI_DISABLE);
		Spi_SetTransferMode(base, mode);
		Spi_Enable(base, SPI_ENABLE);
#ifdef CONFIG_FH_SPI_ECC
		if (p_fh_spi->ecc_status & SPI_XFER_ECC_ENCODE) {
			Spi_SetEccTransferMode(base, SPI_ENABLE);
			Spi_EccEnable(base, SPI_ENABLE);
		}
#endif

#ifdef CONFIG_FH_SPI_DTR
		if (p_fh_spi->dtr_mode) {
			Spi_SetFrameForm(base, OP_3BYTE_ADDR);
			Spi_SetFieldForm(base, OP_SERIAL_QUAD_DATA);
			Spi_SetDTREnable(base, SPI_ENABLE);
 		}
#endif

		while (len) {
			xfer_one_time = tx_max_tx_only(p_fh_spi, len);
			len -= xfer_one_time;
			for (i = 0; i < xfer_one_time; i++) {
				//temp = *txp++;
				SET_REG(data_addr, *txp++);
			}
		}
		/*wait tx done...*/
		do {
			status = Spi_ReadStatus(base);
			/* wait tx fifo and shift not busy.. */
		} while (!(status & 0x04) || (status & 0x01));
#ifdef CONFIG_FH_SPI_ECC
		if (p_fh_spi->ecc_status & SPI_XFER_ECC_ENCODE)
			Spi_EccEnable(base, 0);
#endif
#ifdef CONFIG_FH_SPI_DTR
		if (p_fh_spi->dtr_mode) {
			Spi_SetFrameForm(base, DATA_ONLY);
			Spi_SetFieldForm(base, ALL_SERIAL);
			Spi_SetDTREnable(base, SPI_DISABLE);
		}
#endif
	} else if (mode == SPI_ONLY_RX_MODE) {
		if (slave->mode & SPI_32BIT_XFER) {
			p_32_rx = (UINT32 *)rxp;
			if(len >= DMA_RX_ONLY_THRESHILD){
				spi_32bit_check_data(p_32_rx,len);
				byte_width = 32;
				Spi_SetSwap(base,1);
				Spi_SetWidth(base,byte_width);
			}
		}else if (slave->mode & SPI_16BIT_XFER) {
			p_16_rx = (UINT16 *)rxp;
			if (len >= DMA_RX_ONLY_THRESHILD){
				spi_16bit_check_data(p_16_rx, len);
				byte_width = 16;
				Spi_SetSwap(base, 1);
				Spi_SetWidth(base, byte_width);
			}
		}
		//refix len below..if 16bit open,len should div 2.
		len = len / (byte_width / 8);

#if defined CONFIG_FH_DMA || defined CONFIG_FH_AXI_DMA || defined CONFIG_FH_MC_DMA
#ifdef CONFIG_FH_SPI_ECC
			if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE) {
				Spi_SetDmaInc(base, byte_width);
				Spi_SetEccTransferMode(base, 0);
			}
#endif
#ifdef CONFIG_FH_SPI_DTR
			if (p_fh_spi->dtr_mode) {
				Spi_SetFieldForm(base, ALL_DUAL_QUAD);
				Spi_SetDTREnable(base, SPI_ENABLE);
			}
#endif
			if(len >= DMA_RX_ONLY_THRESHILD) {
				while(index != len){
					otp_xfer_bytes = min(limit, (len - index));
					spi_xfer_dma_rx_only(p_fh_spi,rxp, otp_xfer_bytes);
					rxp += (byte_width / 8) * otp_xfer_bytes;
					index +=otp_xfer_bytes;
				}
			} else {
				do {
					xfer_one_time = min(p_fh_spi->rx_fifo_len, len);
					fh_spi_high_speed_rx_process(p_fh_spi, rxp, xfer_one_time, byte_width);
					len -= xfer_one_time;
					rxp += (byte_width / 8) * xfer_one_time;
				} while (len);
			}
#ifdef CONFIG_FH_SPI_ECC
			if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE) {
				Spi_SetDmaInc(base, 0);
				Spi_EccEnable(base, SPI_DISABLE);
			}
#endif
#ifdef CONFIG_FH_SPI_DTR
			if (p_fh_spi->dtr_mode) {
				Spi_SetFieldForm(base, ALL_SERIAL);
				Spi_SetDTREnable(base, SPI_DISABLE);
			}
#endif
#else
#ifdef CONFIG_FH_SPI_ECC
			if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE) {
				Spi_SetEccTransferMode(base, 0);
				Spi_EccEnable(base, SPI_ENABLE);
				Spi_WaitEccComplete(base);
			}
#endif
			do {
#ifdef CONFIG_FH_SPI_ECC
				if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE)
					xfer_one_time = min(p_fh_spi->rx_fifo_len - 1, len);
				else
#endif
					xfer_one_time = min(p_fh_spi->rx_fifo_len, len);
				fh_spi_high_speed_rx_process(p_fh_spi, rxp, xfer_one_time, byte_width);
				len -= xfer_one_time;
				rxp += (byte_width / 8) * xfer_one_time;
			} while (len);
#ifdef CONFIG_FH_SPI_ECC
			if (p_fh_spi->ecc_status & SPI_XFER_ECC_DECODE)
				Spi_EccEnable(base, SPI_DISABLE);
#endif
#ifdef CONFIG_FH_SPI_DTR
			if (p_fh_spi->dtr_mode) {
				Spi_SetFieldForm(base, ALL_SERIAL);
				Spi_SetDTREnable(base, SPI_DISABLE);
			}
#endif
#endif
		if (slave->mode & SPI_16BIT_XFER || slave->mode & SPI_32BIT_XFER) {
			Spi_SetSwap(base, 0);
			Spi_SetWidth(base, 8);
		}
	}
	else if (mode == SPI_TX_RX_MODE) {
		Spi_Enable(base, SPI_DISABLE);
		Spi_SetTransferMode(base, mode);
		Spi_Enable(base, SPI_ENABLE);
		fh_spi_tx_rx_handle(p_fh_spi, (uchar *)txp, rxp, len);
	}
	return 0;
}

void fix_fh_spi_transfer_mode(struct spi_slave *slave,const void *dout,void *din){
	fh_spi_controller *fh_spi = to_fh_spi(slave);

	if (dout && din) {
			fh_spi->transfer_mode = SPI_TX_RX_MODE;
		} else if (dout) {
			//only tx...
			fh_spi->transfer_mode = SPI_ONLY_TX_MODE;
		} else if (din) {
			//only rx...
			fh_spi->transfer_mode = SPI_ONLY_RX_MODE;
		} else {
			//maybe tx and rx all null
			fh_spi->transfer_mode = SPI_TX_RX_MODE;
		}
	FH_SPI_DEBUG("%s mode:%x, dout:%x, din:%x\n",__func__,fh_spi->transfer_mode,(unsigned int)dout,(unsigned int)din);
}

int fh_spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
                void *din, unsigned long flags)
{
	uint txupto, rxupto;
	uchar *rxp;
	const uchar *txp;

	FH_SPI_DEBUG("sf %s(%x,%d,%x,%x,%d)\n", __func__, slave, bitlen, dout, din,
			flags);
	rxp = din;
	txp = dout;
	fh_spi_controller *fh_spi = to_fh_spi(slave);
	if (bitlen % 8) {
		flags |= SPI_XFER_END;
		goto out;
	}
	spi_release_bus(slave);
	if ((flags & SPI_XFER_BEGIN))
		spi_cs_activate(slave);
	fh_spi->ecc_status = flags & (SPI_XFER_ECC_ENCODE | SPI_XFER_ECC_DECODE);
	fh_spi->dtr_mode   = flags & SPI_XFER_DTR;
	txupto = rxupto = bitlen / 8;
	fix_fh_spi_transfer_mode(slave,dout,din);
	fh_spi_high_speed_xfer(fh_spi, rxp, txp, txupto,fh_spi->transfer_mode);
out:
	if ((flags & SPI_XFER_END))
		spi_cs_deactivate(slave);
	return 0;
}
